﻿using System;
using System.Linq.Expressions;
using System.Text;
using System.Web.Mvc;
using System.Web.Mvc.Html;

namespace CodeContrib.Web.Mvc.Html
{
	public static class EditorExtensions
	{
		public static MvcHtmlString EditorGroup(this HtmlHelper htmlHelper, string expression, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, expression, null, null, null, validationMessage);
		}

		public static MvcHtmlString EditorGroup(this HtmlHelper htmlHelper, string expression, object additionalViewData, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, expression, null, null, additionalViewData, validationMessage);
		}

		public static MvcHtmlString EditorGroup(this HtmlHelper htmlHelper, string expression, string templateName, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, expression, templateName, null, null, validationMessage);
		}

		public static MvcHtmlString EditorGroup(this HtmlHelper htmlHelper, string expression, string templateName, object additionalViewData, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, expression, templateName, null, additionalViewData, validationMessage);
		}

		public static MvcHtmlString EditorGroup(this HtmlHelper htmlHelper, string expression, string templateName, string htmlFieldName, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, expression, templateName, htmlFieldName, null, validationMessage);
		}

		public static MvcHtmlString EditorGroup(this HtmlHelper htmlHelper, string expression, string templateName, string htmlFieldName, object additionalViewData, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, expression, templateName, htmlFieldName, additionalViewData, validationMessage);
		}

		public static MvcHtmlString EditorGroupFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, expression, null, null, null, validationMessage);
		}

		public static MvcHtmlString EditorGroupFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, object additionalViewData, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, expression, null, null, additionalViewData, validationMessage);
		}

		public static MvcHtmlString EditorGroupFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string templateName, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, expression, templateName, null, null, validationMessage);
		}

		public static MvcHtmlString EditorGroupFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string templateName, object additionalViewData, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, expression, templateName, null, additionalViewData, validationMessage);
		}

		public static MvcHtmlString EditorGroupFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string templateName, string htmlFieldName, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, expression, templateName, htmlFieldName, null, validationMessage);
		}

		public static MvcHtmlString EditorGroupFor<TModel, TValue>(this HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string templateName, string htmlFieldName, object additionalViewData, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, expression, templateName, htmlFieldName, additionalViewData, validationMessage);
		}

		internal static MvcHtmlString EditorGroupHelper(HtmlHelper htmlHelper, string expression, string templateName, string htmlFieldName, object additionalViewData, string validationMessage = null)
		{
			var metadata = ModelMetadata.FromStringExpression(expression, htmlHelper.ViewData);

			if (metadata.DisplayName == null)
			{
				metadata.DisplayName = expression;
			}

			return EditorGroupHelper(htmlHelper, metadata, templateName, htmlFieldName ?? ExpressionHelper.GetExpressionText(expression), additionalViewData, validationMessage);
		}

		internal static MvcHtmlString EditorGroupHelper<TModel, TValue>(HtmlHelper<TModel> htmlHelper, Expression<Func<TModel, TValue>> expression, string templateName, string htmlFieldName, object additionalViewData, string validationMessage = null)
		{
			return EditorGroupHelper(htmlHelper, ModelMetadata.FromLambdaExpression(expression, htmlHelper.ViewData), templateName, htmlFieldName ?? ExpressionHelper.GetExpressionText(expression), additionalViewData, validationMessage);
		}

		internal static MvcHtmlString EditorGroupHelper(HtmlHelper htmlHelper, ModelMetadata metadata, string templateName, string htmlFieldName, object additionalViewData, string validationMessage = null)
		{
			using (htmlHelper.ControlGroup(htmlFieldName))
			{
				var displayName = metadata.GetDisplayName();
				var label = htmlHelper.Label(htmlFieldName, (metadata.IsRequired) ? string.Format("{0} *", displayName) : displayName, new
					                                                                                                                       {
						                                                                                                                       @class = "control-label"
					                                                                                                                       }).ToHtmlString();

				htmlHelper.ViewContext.Writer.Write(label);

				var innerHtml = new StringBuilder();
				innerHtml.Append(htmlHelper.Editor(htmlFieldName, templateName, additionalViewData).ToHtmlString());
				innerHtml.Append(htmlHelper.ValidationMessage(htmlFieldName, validationMessage, new
					                                                                                {
						                                                                                @class = "help-inline"
					                                                                                }).ToHtmlString());

				var controls = new TagBuilder("div")
					               {
						               InnerHtml = innerHtml.ToString()
					               };
				controls.AddCssClass("controls");

				htmlHelper.ViewContext.Writer.Write(controls);
			}

			return MvcHtmlString.Empty;
		}
	}
}